@mapled/mcp 0.18.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +63 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,11 +63,12 @@ A rename or a conversion Mapled wouldn't take is refused at once with the reason
|
|
|
63
63
|
| Tool | What it does |
|
|
64
64
|
| --- | --- |
|
|
65
65
|
| `propose_setup_plan` / `get_setup_run` / `apply_setup_plan` / `report_setup` / `verify_setup` | One approved plan for the whole setup — every field type including relations (`$ref` handles between plan records, ids for existing collections) and groups — verified by Mapled (see above) |
|
|
66
|
-
| `get_schema` | Read the project's collections and fields |
|
|
66
|
+
| `get_schema` | Read the project's collections and fields, and its component types (`componentTypes`) |
|
|
67
67
|
| `create_collection` | Add a collection or single |
|
|
68
|
-
| `add_field` | Add a field (short_text, long_text, rich_text, slug, image, number, boolean, date, datetime, relation, enum, url, email, group, file, color, json — an object or list up to 32 KB, location — { lat, lng }, computed). Optional `validation` ({min, max, pattern}) and `defaultValue`; `relation` ({target, cardinality: one \| many, onDelete: restrict \| nullify}) is required for relation fields — values are record ids of the target collection, kept in the order given; `onDelete` says what a delete of a linked record does (restrict: it can't be deleted while linked, nullify: the links are cleared — the default is restrict for required fields and nullify otherwise); `options` (1–50 labels) is required for enum fields; `sensitive: true` keeps a field out of lists, history and delivery (a group's sub-fields take it too); `group` ({fields, repeatable, maxItems}) shapes a group field — its values are objects (or arrays of them) keyed by the sub-field keys; `computed` ({expression}) makes a computed field — a formula Mapled evaluates whenever a record is read or published, over the record's fields and up to two links through relations (`author.company.name`, `sum(items.product.price)`) or back along one and one link on (`count(@posts.author)`, `sum(@order-items.order.product.price)`), at most one list per path, never a sensitive field or relation; `today()` and `now()` are the moment the value was computed — baked at publish and recomputed once a day for the current release; the site reads the value like any field of its result type. |
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
68
|
+
| `add_field` | Add a field (short_text, long_text, rich_text, slug, image, number, boolean, date, datetime, relation, enum, url, email, group, file, color, json — an object or list up to 32 KB, location — { lat, lng }, computed, components). Optional `validation` ({min, max, pattern}) and `defaultValue`; `relation` ({target, cardinality: one \| many, onDelete: restrict \| nullify}) is required for relation fields — values are record ids of the target collection, kept in the order given; `onDelete` says what a delete of a linked record does (restrict: it can't be deleted while linked, nullify: the links are cleared — the default is restrict for required fields and nullify otherwise); `options` (1–50 labels) is required for enum fields; `sensitive: true` keeps a field out of lists, history and delivery (a group's sub-fields take it too); `group` ({fields, repeatable, maxItems}) shapes a group field — its values are objects (or arrays of them) keyed by the sub-field keys; `components` ({allowed, min, max}) makes a components field — an ordered list of blocks of the component types named in `allowed` (their keys, from `add_component_type`); values are lists of `{ _type, _key, …sub-fields }`, `_key` given by Mapled and kept when sent back; `computed` ({expression}) makes a computed field — a formula Mapled evaluates whenever a record is read or published, over the record's fields and up to two links through relations (`author.company.name`, `sum(items.product.price)`) or back along one and one link on (`count(@posts.author)`, `sum(@order-items.order.product.price)`), at most one list per path, never a sensitive field or relation; `today()` and `now()` are the moment the value was computed — baked at publish and recomputed once a day for the current release; the site reads the value like any field of its result type. |
|
|
69
|
+
| `add_component_type` | Add a component type — a reusable block (hero, text, gallery…) for `components` fields: a name and the sub-fields an item holds (the types a group's sub-fields take, none sensitive, no relation or components inside); its key is what an item names in `_type`; `types generate` writes one TypeScript type per component and a components field as a union of them |
|
|
70
|
+
| `add_records` | Insert draft records — a translated field (`localized: true` in `get_schema`) by language, `{ "en": "About us", "ru": "О нас" }`, or as one plain value, the default language's |
|
|
71
|
+
| `list_records` | Read a collection's draft records, newest edit first — `query` searches their content as full text (every word, the last one from its start; best match first), `limit` (1–200) and `cursor` (the previous answer's `nextCursor`) page through them; `total` counts every match; `locale` reads the translated fields in one language (`ru`) or in every one at once (`*`, as `{ "en": …, "ru": … }`) |
|
|
71
72
|
| `create_form` / `list_forms` | Set up public forms with spam protection |
|
|
72
73
|
| `get_connection` | The delivery key and the API URL, plus — for the `framework` the agent names (`nextjs`, `react-spa`, `plain-html`, …) or the project's own — the package the site reads through (`@mapled/next`, `@mapled/react`, `@mapled/vanilla` — a script tag and `data-mapled-*` attributes for plain HTML — or `@mapled/client`), the env var of the key, whether the site renders on a server or in the browser, and the wire-up steps. Image values → `assetUrl(id, { width })` of the same package |
|
|
73
74
|
| `set_site_url` | Where the site is deployed — for a site rendered in the browser (a React single-page app, plain HTML), which has no webhook to learn it from: Preview opens the site there, verification checks that it answers |
|
package/dist/tools.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export type ApiClient = {
|
|
|
7
7
|
/** What a call answers when its request never reached Mapled. */
|
|
8
8
|
export declare const UNREACHABLE = "The request didn't reach Mapled. Try again, or tell the person if it keeps failing: MAPLED_API_URL or MAPLED_MCP_TOKEN in the MCP settings may need a fresh copy. Don't ask them to paste the token into the conversation.";
|
|
9
9
|
export declare function createApiClient(baseUrl: string, token: string): ApiClient;
|
|
10
|
-
export declare const FIELD_TYPES: readonly ["short_text", "long_text", "rich_text", "slug", "image", "number", "boolean", "date", "relation", "enum", "url", "email", "group", "datetime", "file", "color", "json", "location", "computed"];
|
|
10
|
+
export declare const FIELD_TYPES: readonly ["short_text", "long_text", "rich_text", "slug", "image", "number", "boolean", "date", "relation", "enum", "url", "email", "group", "datetime", "file", "color", "json", "location", "computed", "components"];
|
|
11
11
|
export type ToolDef = {
|
|
12
12
|
name: string;
|
|
13
13
|
description: string;
|
package/dist/tools.js
CHANGED
|
@@ -49,7 +49,11 @@ export const FIELD_TYPES = [
|
|
|
49
49
|
"json",
|
|
50
50
|
"location",
|
|
51
51
|
"computed",
|
|
52
|
+
"components",
|
|
52
53
|
];
|
|
54
|
+
/** The sub-field types a component (§45.4) may hold — a group's, none
|
|
55
|
+
sensitive, no relation or components inside. */
|
|
56
|
+
const COMPONENT_SUB_FIELD_TYPES = ["short_text", "long_text", "rich_text", "image", "number", "boolean", "date", "url", "email", "enum", "datetime", "file", "color", "json", "location"];
|
|
53
57
|
/** What add_field and propose_setup_plan say about formulas (§14.9). */
|
|
54
58
|
const COMPUTED_HELP = "For type computed only: { expression } — a formula over the record's other fields that Mapled evaluates when a record is read or published (read-only for editors and agents; the site reads the value like any field of the result type, filters and sorts included; writes ignore it). " +
|
|
55
59
|
"Field keys as written (price, unit-cost — put spaces around a minus to subtract: price - cost); up to two links through relations: author.name, author.company.name, and lists over many-relations for aggregates: sum(items.price), count(tags), join(tags.name, \", \"), sum(items.product.price); or one link back — the records of another collection whose relation points at this record, written @collection.relation[.field], always a list, oldest first: count(@posts.author), sum(@order-items.order.total), max(@posts.author.published-on) — and one more link on from them: sum(@order-items.order.product.price). A path goes through at most one list (a many-relation, or the records that link back), so items.tags.name is refused. " +
|
|
@@ -203,7 +207,7 @@ export function createTools(api) {
|
|
|
203
207
|
return [
|
|
204
208
|
{
|
|
205
209
|
name: "get_schema",
|
|
206
|
-
description: "Read the project's full content schema: collections with their fields (a computed field carries its formula and result type under `computed`)
|
|
210
|
+
description: "Read the project's full content schema: collections with their fields (a computed field carries its formula and result type under `computed`; a components field names the component types it allows under `components`), and the project's component types under `componentTypes`.",
|
|
207
211
|
schema: {},
|
|
208
212
|
handler: async () => api.request("GET", "/v1/agent/schema"),
|
|
209
213
|
},
|
|
@@ -458,6 +462,13 @@ export function createTools(api) {
|
|
|
458
462
|
"script tag) sends { realtime: true }; a site without live updates sends { realtime: false }. Only these two keys " +
|
|
459
463
|
"are accepted. Nothing is graded by them and they stay out of the integration hash, but MAPLED.md's " +
|
|
460
464
|
"Live updates line comes from them — send them with every push, as a push without them drops the line. " +
|
|
465
|
+
"`locales` says how the routes carry the language on a site with more than one (§28.4): " +
|
|
466
|
+
"{ routing: \"prefix\", param: \"locale\" } when every language lives under its own segment " +
|
|
467
|
+
"(/[locale]/blog/[slug] — the page routes keep the segment), { routing: \"prefix-except-default\" } when the " +
|
|
468
|
+
"default language is at the root and the others under /ru/…, /de/… (without `param` Mapled puts the language " +
|
|
469
|
+
"before the page). Mapled builds each language's address of a record from it (the editor's routes, Preview); " +
|
|
470
|
+
"a translated slug is the address in that language, a missing translation falls back. Leave it out on a " +
|
|
471
|
+
"site with one address for every language. " +
|
|
461
472
|
"The answer's manifest.integrationHash is the hash of the schema and these " +
|
|
462
473
|
"bindings — what the site is synced with from now on; check_integration reports inSync against it. " +
|
|
463
474
|
"Needs the builder plan.",
|
|
@@ -499,6 +510,15 @@ export function createTools(api) {
|
|
|
499
510
|
.strict()
|
|
500
511
|
.optional()
|
|
501
512
|
.describe("Whether open tabs follow a publish: realtime, and releaseRoute — the path of the site's release route, e.g. /api/mapled/release."),
|
|
513
|
+
// routes by language (§28.4), the API's shape (lib/bindings.ts): strict
|
|
514
|
+
locales: z
|
|
515
|
+
.object({
|
|
516
|
+
routing: z.enum(["prefix", "prefix-except-default"]),
|
|
517
|
+
param: z.string().regex(/^[A-Za-z0-9_-]{1,60}$/).optional(),
|
|
518
|
+
})
|
|
519
|
+
.strict()
|
|
520
|
+
.optional()
|
|
521
|
+
.describe("How the routes carry the language: routing — prefix (every language under its own) or prefix-except-default (the default at the root); param — the dynamic segment that holds it, e.g. locale for /[locale]/blog/[slug]."),
|
|
502
522
|
},
|
|
503
523
|
handler: async (args) => api.request("POST", "/v1/agent/manifest", args),
|
|
504
524
|
},
|
|
@@ -534,7 +554,7 @@ export function createTools(api) {
|
|
|
534
554
|
},
|
|
535
555
|
{
|
|
536
556
|
name: "add_field",
|
|
537
|
-
description: "Add a field to a collection. Types: short_text, long_text, rich_text (Markdown: headings, lists, links, bold/italic, images as ), slug, image, number, boolean, date, relation (a link to records of another collection: pass `relation`; values are record ids), enum (a choice: pass `options`), url, email, group (an object shaped by its own `group.fields`, or a list of them when repeatable — feature cards, FAQ items; values are objects / arrays of objects keyed by the sub-field keys), datetime (ISO 8601, stored in UTC), file (an asset id of any uploaded file), color (#rrggbb), json (any object or list up to 32 KB — settings, specs, structured data the site reads as is), location ({ lat, lng } in degrees), computed (a value derived from the record's other fields: pass `computed.expression`; never required or sensitive).",
|
|
557
|
+
description: "Add a field to a collection. Types: short_text, long_text, rich_text (Markdown: headings, lists, links, bold/italic, images as ), slug, image, number, boolean, date, relation (a link to records of another collection: pass `relation`; values are record ids), enum (a choice: pass `options`), url, email, group (an object shaped by its own `group.fields`, or a list of them when repeatable — feature cards, FAQ items; values are objects / arrays of objects keyed by the sub-field keys), datetime (ISO 8601, stored in UTC), file (an asset id of any uploaded file), color (#rrggbb), json (any object or list up to 32 KB — settings, specs, structured data the site reads as is), location ({ lat, lng } in degrees), computed (a value derived from the record's other fields: pass `computed.expression`; never required or sensitive), components (an ordered list of blocks of the project's component types — add_component_type first, then pass `components.allowed` with their keys; values are lists of { _type, _key?, …sub-fields }; never sensitive).",
|
|
538
558
|
schema: {
|
|
539
559
|
collectionKey: z.string().min(1).max(120),
|
|
540
560
|
displayName: z.string().min(1).max(120),
|
|
@@ -599,6 +619,14 @@ export function createTools(api) {
|
|
|
599
619
|
.optional()
|
|
600
620
|
.describe("For type group only. Sub-field keys are the lowercase, hyphenated names."),
|
|
601
621
|
computed: z.object({ expression: z.string().min(1).max(500) }).optional().describe(COMPUTED_HELP),
|
|
622
|
+
components: z
|
|
623
|
+
.object({
|
|
624
|
+
allowed: z.array(z.string().min(1).max(50)).min(1).max(30).describe("Keys of the component types (add_component_type, get_schema's componentTypes) an item may be."),
|
|
625
|
+
min: z.number().int().min(0).max(100).optional().describe("The fewest items a value holds (0 by default)."),
|
|
626
|
+
max: z.number().int().min(1).max(100).optional().describe("The most items a value holds (100 by default)."),
|
|
627
|
+
})
|
|
628
|
+
.optional()
|
|
629
|
+
.describe("Required for type components, not allowed otherwise: the component types the list may hold and how many items."),
|
|
602
630
|
},
|
|
603
631
|
handler: async (args) => api.request("POST", `/v1/agent/collections/${encodeURIComponent(args.collectionKey)}/fields`, {
|
|
604
632
|
displayName: args.displayName,
|
|
@@ -612,11 +640,35 @@ export function createTools(api) {
|
|
|
612
640
|
...(args.sensitive !== undefined ? { sensitive: args.sensitive } : {}),
|
|
613
641
|
...(args.group ? { group: args.group } : {}),
|
|
614
642
|
...(args.computed ? { computed: args.computed } : {}),
|
|
643
|
+
...(args.components ? { components: args.components } : {}),
|
|
615
644
|
}),
|
|
616
645
|
},
|
|
646
|
+
{
|
|
647
|
+
name: "add_component_type",
|
|
648
|
+
description: "Add a component type to the project — a reusable block for components fields (hero, text, gallery…): a name and the sub-fields an item of it holds (the types a group's sub-fields take; none sensitive, no relation or components inside; a type may have no fields at all, a divider). " +
|
|
649
|
+
"Its key is the lowercase, hyphenated name — items of a components field name it in `_type`; pass it in `components.allowed` of add_field. get_schema lists the project's types under componentTypes.",
|
|
650
|
+
schema: {
|
|
651
|
+
displayName: z.string().min(1).max(120),
|
|
652
|
+
fields: z
|
|
653
|
+
.array(z.object({
|
|
654
|
+
displayName: z.string().min(1).max(120),
|
|
655
|
+
type: z.enum(COMPONENT_SUB_FIELD_TYPES),
|
|
656
|
+
required: z.boolean().optional(),
|
|
657
|
+
helpText: z.string().max(500).optional(),
|
|
658
|
+
options: z.array(z.string().min(1).max(60)).min(1).max(50).optional().describe("For type enum only."),
|
|
659
|
+
})
|
|
660
|
+
// no `sensitive` here: a component's values go to the site as they are
|
|
661
|
+
.strict())
|
|
662
|
+
.max(20)
|
|
663
|
+
.optional()
|
|
664
|
+
.describe("The sub-fields of an item, in order; keys are the lowercase, hyphenated names."),
|
|
665
|
+
},
|
|
666
|
+
handler: async (args) => api.request("POST", "/v1/agent/component-types", { displayName: args.displayName, fields: args.fields ?? [] }),
|
|
667
|
+
},
|
|
617
668
|
{
|
|
618
669
|
name: "add_records",
|
|
619
|
-
description: "Insert up to 100 records into a collection. Each record maps field keys to values (rich_text takes Markdown; image takes an asset id; relation takes record ids; group takes objects)."
|
|
670
|
+
description: "Insert up to 100 records into a collection. Each record maps field keys to values (rich_text takes Markdown; image takes an asset id; relation takes record ids; group takes objects; components takes a list of items, each { _type: \"<component key>\", …its sub-fields } — Mapled gives every item a _key, kept when sent back). " +
|
|
671
|
+
'A translated field (`localized: true` in get_schema) takes its values by language — { "en": "About us", "ru": "О нас" }; give the default language, it is what the site shows where a translation is missing — or one plain value, which is the default language\'s; a translated slug is unique within its language.',
|
|
620
672
|
schema: {
|
|
621
673
|
collectionKey: z.string().min(1).max(120),
|
|
622
674
|
records: z.array(z.record(z.string(), z.unknown())).min(1).max(100),
|
|
@@ -644,12 +696,14 @@ export function createTools(api) {
|
|
|
644
696
|
},
|
|
645
697
|
{
|
|
646
698
|
name: "list_records",
|
|
647
|
-
description: "List a collection's draft records (id, title, data), most recently edited first — up to `limit` per page (200 by default). `query` narrows the list to records whose content contains it; when the answer carries nextCursor, pass it as `cursor` for the next page. `total` counts every match."
|
|
699
|
+
description: "List a collection's draft records (id, title, data), most recently edited first — up to `limit` per page (200 by default). `query` narrows the list to records whose content contains it; when the answer carries nextCursor, pass it as `cursor` for the next page. `total` counts every match. A components field reads as its list of items, each with its _type and _key. " +
|
|
700
|
+
"`locale` reads the translated fields in one language (a code such as ru — a field with no value in that language is left out) or, with *, in every language at once as { \"<code>\": value }; without it the default language, as always.",
|
|
648
701
|
schema: {
|
|
649
702
|
collectionKey: z.string().min(1).max(120),
|
|
650
703
|
query: z.string().max(200).optional().describe("Text to search for in the records' content."),
|
|
651
704
|
limit: z.number().int().min(1).max(200).optional().describe("Records per page, 1–200 (default 200)."),
|
|
652
705
|
cursor: z.string().max(200).optional().describe("nextCursor from the previous page."),
|
|
706
|
+
locale: z.string().max(40).optional().describe("A language code such as ru or pt-BR, or * for every language at once."),
|
|
653
707
|
},
|
|
654
708
|
handler: async (args) => {
|
|
655
709
|
const params = new URLSearchParams();
|
|
@@ -659,6 +713,8 @@ export function createTools(api) {
|
|
|
659
713
|
params.set("limit", String(args.limit));
|
|
660
714
|
if (args.cursor)
|
|
661
715
|
params.set("cursor", args.cursor);
|
|
716
|
+
if (args.locale)
|
|
717
|
+
params.set("locale", args.locale);
|
|
662
718
|
const qs = params.toString();
|
|
663
719
|
return api.request("GET", `/v1/agent/collections/${encodeURIComponent(args.collectionKey)}/records${qs ? `?${qs}` : ""}`);
|
|
664
720
|
},
|
|
@@ -671,7 +727,9 @@ export function createTools(api) {
|
|
|
671
727
|
'`framework` as you see it in the repository: "nextjs" (@mapled/next), "react-spa" for a React ' +
|
|
672
728
|
'single-page app such as Vite (@mapled/react), "plain-html" for pages without a build step ' +
|
|
673
729
|
"(@mapled/vanilla: one script tag and data-mapled-* attributes on the elements — no reading code to " +
|
|
674
|
-
|
|
730
|
+
'write, wireUp lists the attributes), "astro" (@mapled/astro: the same reads plus the webhook, preview and ' +
|
|
731
|
+
'live routes in Astro\'s idioms), "nuxt" (@mapled/nuxt: the module mounts the routes, useMapled() reads), ' +
|
|
732
|
+
"or another short key (remix, sveltekit, … — " +
|
|
675
733
|
"@mapled/client on the server); left out, the project's own is used. The JavaScript packages have the same reads: " +
|
|
676
734
|
'createClient({ key }).getRecords("<collection>"), getSingle, getRecordBySlug. ' +
|
|
677
735
|
"Reads take filter ({ field: value } or { field: { gte, lt, in, contains… } }), sort, limit/offset, " +
|